package de.servicezombie.lang;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.apache.cxf.common.util.StringUtils;
import org.springframework.beans.BeanUtils;

public abstract class Assert {

	public static void assertNotNull(Object bean, String beanName) {
		if (bean == null) {
			throw new IllegalArgumentException("null check failed for '" + beanName + "'");
		}
	}

	public static void assertNotNull(Object bean) {
		assertNotNull(bean, "unknown variable");
	}

	public static void assertNotEmpty(String bean, String beanName) {
		if (StringUtils.isEmpty(bean)) {
			throw new IllegalArgumentException("Emptiness check failed for '" + beanName + "'");
		}
	}

	public static void assertNotEmpty(String bean) {
		assertNotEmpty(bean, "unknown variable");
	}

	public static <T> void assertEqualProperty(T leftBean, T rightBean, String property) {

		Object actualValue = getPropertyValue(leftBean, property);
		Object expectedValue = getPropertyValue(rightBean, property);

		if (actualValue == null && expectedValue == null) {
			return;
		}

		if (actualValue != null && expectedValue == null) {
			buildException(rightBean.getClass(), property, actualValue, expectedValue);
		}

		if (actualValue == null && expectedValue != null) {
			buildException(rightBean.getClass(), property, actualValue, expectedValue);
		}

		if (!actualValue.equals(expectedValue)) {
			buildException(rightBean.getClass(), property, actualValue, expectedValue);
		}

	}

	private static void buildException(Class<?> type, String propertyName, Object leftValue, Object rightValue) {
		throw new IllegalArgumentException(leftValue + " not equals " + rightValue + " in " + type.getName() + "#" + propertyName);
	}

	private static Object getPropertyValue(Object bean, String propertyName) {

		assertNotNull(bean);

		PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(bean.getClass(), propertyName);
		assertNotNull(propertyDescriptor, "Property " + propertyName + " is not available for " + bean.getClass().getName());

		Method readerForActualProperty = propertyDescriptor.getReadMethod();
		assertNotNull(readerForActualProperty, "No property available for " + bean.getClass() + "." + propertyName);

		try {
			Object value = readerForActualProperty.invoke(bean);
			return value;
		}
		catch (InvocationTargetException e) {
			throw new WrappedException(e.getCause());
		}
		catch (Exception e) {
			throw new WrappedException(e);
		}
	}

}
